/**
 * 1.指针   : 指针也是一个变量, 存放 变量地址, 该地址指向一块内存空间.
 *   指针变量: 指向一个变量的指针.
 *
 * 2.计算机的内存最小单位是Byte, 每一个Byte的内存都有一个唯一的编号, 这个编号就是内存地址.
 *    编号在32位系统下是一个32位的整数, 在64位系统下是一个64位的整数.
 *
 * 3.&取地址运算符, &可以取得一个变量在内存中的地址
 *
 * 7.指针的兼容性
 *   指针之间赋值比普通数据类型赋值更为严格, 例如: 不可以把一个 double *赋值给 int(虽然不报错)
 */
#include <stdio.h>

void pointer();
void wild_pointer();
void null_pointer();
void void_pointer();
void const_pointer();
void level2_printer();
void function_pointer();

int main() {
    //1.指针一般使用
    pointer();
    //2.野指针
    wild_pointer();
    //3.空指针
    null_pointer();
    //4.万能指针
    void_pointer();
    //5.const, 指向常量的指针, 常量指针, 指针常量, 指向常量的常量指针
    const_pointer();
    //6. 2级指针
    level2_printer();
    // .函数指针: 指向函数的指针
    function_pointer();

    return 0;
}

//1.指针一般使用
void pointer() {
    printf("1.指针一般使用\n");
    int a = 10;
    int *p = &a;                  //指针p, 存储变量a的地址值
    size_t len = sizeof(p);
    printf("指针长度 = %d\n", len);//4 (32位系统)
    printf("a地址 = %p\n", &a);   //0061FED0
    printf("*p1的值 = %p\n", p);  //0061FED0, 打印p的值(存的是a的地址值)

    *p = 100;                    //通过指针改变变量的值 = 100
    printf("a = %d\n", a);       //100
    printf("*p = %d\n", *p);     //100
}

/**
 * 2.野指针
 *   野指针: 擅自访问某个地址, 可能报错
 *   操作系统将0~255作为系统占用, 不允许访问, 会报错
 */
void wild_pointer() {
    int *p1;
    int v = *p1;        //1.指针未初始化就使用, 会报错!!!

    //2.随意赋值
    int *p2 = 0xaabbccdd;
}

/**
 * 3.空指针, NULL: 内存地址=0的空间
 *   #define NULL ((void *)0)
 */
void null_pointer() {
    int *p = NULL;
//    printf("*p = %d\n", *p);    //0, 读取&写入 报错
}



/**
 * 4.万能指针, 无类型指针: https://www.bilibili.com/video/BV1vs411n7TH/?p=62
 *   void *指针可以指向任意变量的内存空间.
 *   不指定它指向具体哪种数据类型, 可以通过强制转换将 void * 转换为其它类型指针,
 *   也可以用 (void *) 将其它类型指针强制转换为 void 类型指针.
 */
void void_pointer() {
    printf("4.万能指针\n");
    int a = 10;
    void *p = &a;
    *(int *)p = 100;        //修改a的值, 需要强制类型转换
    printf("a = %d\n", a);  //100
}

/**
 * 5. https://www.bilibili.com/video/BV1vs411n7TH/?p=63
 * 定义               名称             可读 可写 可指向第2地址 备注
 * const int a; &a;   指向常量的指针    √    ✔    ✔         const 局部常量不安全, 可通过指针修改.('全局'常量安全!!)
 * const int *p       常量指针         ✔    ✘    ✔        const 在*左边, 修饰的是int, ∴int值不可修改.
 * int const *p       ↑
 * int *const p       指针常量         ✔    ✔    ✘        const 在*右边, 修饰的是指针p, ∴指针不可修改.
 * const int* const p 指向常量的常量指针 ✔    ✘    ✘        只读指针
 * @see
 */
void const_pointer() {
    printf("5.const, 指向常量的指针, 常量指针, 指针常量, 指向常量的常量指针\n");
    const int a = 10;
    int b = 20;
    //1.指针间接修改 常量的值
    int *p1 = &a;
    *p1 = 100;              //可写
    printf("a = %d\n", a);  //100, 可读
    p1 = &b;                //可指向其它地址


    //2.常量指针, const int *p
    const int *p2 = &b;      //一样的写法: int const *p2 = &b;
    //*p2 = 20;              //★★★报错, 不能通过 *p 修改值, 不可写
    printf("b = %d\n", *p2); //20, 可读
    p2 = &a;                 //100, 可指向其它地址

    //3.指针常量
    int *const p3 = &a;      //定义一个"指针常量", 一旦初始化后其内容不可改变
    *p3 = 200;               //可写
    printf("a = %d\n", a);   //200, 可读
//    p3 = &b;               //★★★报错, 不可指向其它地址

    //4.指向常量的常量指针
    const int* const p4 = &a;
//    *p4 = 300;               //★★★报错, 不可写
    printf("*p4 = %d\n", *p4); //200, 可读
//    p4 = &b;                 //★★★报错, 不可指向其它地址
}

//6. 2级指针
void level2_printer() {
    printf("6. 2级指针\n");
    int a = 10, b = 20;
    const int *const p1 = &a;   //只能读, 不能写&指向其它地址

    int **pp = &p1;             //2级指针, 指向p1的地址
    *pp = &b;                   //将*pp(p1地址值) --> 指向b的地址, 可指向其它地址
    printf("*p1 = %d\n", *p1);

    **pp = 30;                  //2级指针直接修改值
    printf("*p1 = %d\n", *p1);
}

// .函数指针: 指向函数的指针
void function_pointer() {

}
